#include "can_plugin.h"
#include "can_device.h"
#include "can_manage.h"
#include <QDataStream>

#define DF_NOTIFY_HEARTBEAT         "heartBeat"
#define DF_NOTIFY_FLUOR_DATA        "fl_data"

/*
 * Function:    CanPlugin
 * Description: default constructor
 * Paramer:     QObject *parent - the point of parent
 * Return:      none
 */
CanPlugin::CanPlugin(QObject *parent)
{
    qDebug() << Q_FUNC_INFO;
    m_pParent = parent;

    Q_ASSERT(CanDevice::getInstance(this));
    Q_ASSERT(CanManage::getInstance(this));

    CanDevice::getInstance(this)->moveToThread(&m_thrDeviceWorker);
    CanManage::getInstance(this)->moveToThread(&m_thrManageWorker);

    bool bConnA = QObject::connect(&m_thrDeviceWorker, &QThread::finished, CanDevice::getInstance(this), &QObject::deleteLater);
    bool bConnB = QObject::connect(&m_thrManageWorker, &QThread::finished, CanManage::getInstance(this), &QObject::deleteLater);
    Q_ASSERT(bConnA && bConnB);

    // enable can0
#ifdef QT_BITRATE_500K
    ::system("ip link set can0 down");
    ::system(QString("ip link set can0 type can bitrate %1").arg(DEF_BITRATE).toLatin1());
    ::system(QString("echo %1 > /sys/class/net/can0/tx_queue_len").arg(1024 * 4).toLatin1());
    ::system("ip link set can0 up");
#else
    ::system("ifconfig can0 down");
    ::system(QString("ip link set can0 up type can bitrate %1 sample-point 0.75 dbitrate 1000000 fd on").arg(DEF_BITRATE).toLatin1());
    ::system("echo 4096 > /sys/class/net/can0/tx_queue_len");
#endif
}

/*
 * Function:    openCAN
 * Description: open a CAN
 * Paramer:     void
 * Return:      int - the error code
 */
int CanPlugin::openCAN()
{
    // Signal-slot initialization
    std::vector<bool> vecConnect;
    vecConnect.push_back(QObject::connect(this, SIGNAL(signalSendFrameData(ST_CAN_FRAME, bool)), this, SLOT(slotSendFrameData(ST_CAN_FRAME, bool)), static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection)));
    vecConnect.push_back(QObject::connect(CanDevice::getInstance(this), SIGNAL(signalFramesReceived(unsigned int, QByteArray)), CanManage::getInstance(this), SLOT(slotFramesReceived(unsigned int, QByteArray)), static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection)));
    vecConnect.push_back(QObject::connect(CanManage::getInstance(this), SIGNAL(finished()), this, SLOT(slotDeleteThread()), static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection)));
    vecConnect.push_back(QObject::connect(CanManage::getInstance(this), SIGNAL(signalSendFrameData(unsigned int, const QByteArray &, bool)), this, SLOT(slotSendFrameData(unsigned int, const QByteArray &, bool)), static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection)));
    vecConnect.push_back(QObject::connect(CanManage::getInstance(this), SIGNAL(signalDataReceived(unsigned int, QByteArray)), this, SLOT(slotDataReceived(unsigned int, QByteArray)), static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection)));
    for(int i=0; i<(int)vecConnect.size(); i++) {
      Q_ASSERT(vecConnect.at(i));
    }

    int nRetVal = CanDevice::getInstance(this)->openCAN();
    if(0 == nRetVal) {
        CanManage::getInstance(this)->startThread();
    }

    qDebug() << Q_FUNC_INFO << nRetVal;
    return nRetVal;
}

/*
 * Function:    closeCAN
 * Description: close a CAN
 * Paramer:     void
 * Return:      void
 */
void CanPlugin::closeCAN()
{
    qDebug() << Q_FUNC_INFO;

    QObject::disconnect(this, SIGNAL(signalSendFrameData(ST_CAN_FRAME, bool)), this, SLOT(slotSendFrameData(ST_CAN_FRAME, bool)));
    QObject::disconnect(CanDevice::getInstance(this), SIGNAL(signalFramesReceived(unsigned int, QByteArray)), CanManage::getInstance(this), SLOT(slotFramesReceived(unsigned int, QByteArray)));
    QObject::disconnect(CanManage::getInstance(this), SIGNAL(finished()), this, SLOT(slotDeleteThread()));
    QObject::disconnect(CanManage::getInstance(this), SIGNAL(signalSendFrameData(unsigned int, const QByteArray &, bool)), this, SLOT(slotSendFrameData(unsigned int, const QByteArray &, bool)));
    QObject::disconnect(CanManage::getInstance(this), SIGNAL(signalDataReceived(unsigned int, QByteArray)), this, SLOT(slotDataReceived(unsigned int, QByteArray)));


    CanManage::getInstance(this)->stopThread();
    CanDevice::getInstance(this)->closeCAN();
}

/*
 * Function:    sendFrameData
 * Description: send an frame data to CAN bus
 * Paramer:     ST_CAN_FRAME stCanFrame - frame data
 * Return:      void
 */
int CanPlugin::sendFrameData(ST_CAN_FRAME stCanFrame, bool bQueued)
{
    qDebug() << Q_FUNC_INFO << stCanFrame.nFrameId << stCanFrame.baFrameData.size() << stCanFrame.baFrameData.toHex(' ').toUpper() << bQueued << stCanFrame.baFrameData;

    int nRetVal = CanDevice::getInstance(this)->sendFrameData(stCanFrame.nFrameId, stCanFrame.baFrameData, bQueued);

    qDebug() << Q_FUNC_INFO << nRetVal;
    return nRetVal;
}

/*
 * Function:    slotSendFrameData
 * Description: Send an frame data to CAN bus
 * Paramer:     ST_CAN_FRAME stCanFrame - can frame
 *              bool bQueued - is block ?
 * Return:      void
 */
int CanPlugin::slotSendFrameData(ST_CAN_FRAME stCanFrame, bool bQueued)
{
    qDebug() << Q_FUNC_INFO << stCanFrame.nFrameId << stCanFrame.baFrameData.size() << bQueued;

    // send frames data to CAN bus
    return CanDevice::getInstance(this)->sendFrameData(stCanFrame.nFrameId, stCanFrame.baFrameData, bQueued);
}

/*
 * Function:    slotSendFrameData
 * Description: Send an frame data to CAN bus
 * Paramer:     unsigned int nFrameId - frame ID
 *              const QByteArray &baFrameData - frame content
 *              bool bQueued - is block ?
 * Return:      void
 */
int CanPlugin::slotSendFrameData(unsigned int nFrameId, const QByteArray &baFrameData, bool bQueued)
{
    qDebug() << Q_FUNC_INFO << nFrameId << baFrameData.size() << bQueued;

    // send frames data to CAN bus
    return CanDevice::getInstance(this)->sendFrameData(nFrameId, baFrameData, bQueued);
}

/*
 * Function:    slotDeleteThread
 * Description: Close thread after finish
 * Paramer:     void
 * Return:      void
 */
void CanPlugin::slotDeleteThread()
{
    qDebug() << Q_FUNC_INFO;

   CanManage::getInstance(this)->wait();
   CanManage::getInstance(this)->deleteLater();
}

/*
 * Function:    slotDataReceived
 * Description: Received data from COM
 * Paramer:     unsigned int unCanId - CAN ID
 *              QByteArray baPacket - the data buffered
 * Return:      void
 */
void CanPlugin::slotDataReceived(unsigned int unCanId, QByteArray baPacket)
{
    qDebug() << Q_FUNC_INFO << unCanId << baPacket.size() << baPacket;

    emit signalPayloadReceived(unCanId, baPacket);
}
